`timescale 1ns / 1ns

// Description: LED Driver
// Author: JerryTech
// License: GPLv3

module led_driver #(
    parameter SYS_FREQ = 'd3072000,
    parameter BREATH_CYCLE_SEC = 'd4
)
(
    input   wire    sys_clk,
    input   wire    reset,

    input   wire    fifo_empty,
    input   wire    usb_rstn,

    output  reg     led_red,
    output  reg     led_green,
    output  reg     led_blue
);

localparam PWM_DIV = 'd1024;
localparam PWM_CLK_DIV = SYS_FREQ * BREATH_CYCLE_SEC / PWM_DIV / PWM_DIV ; 

reg [9 :0] clk_div_counter;
reg [9 :0] pwm_value_counter;
reg [9 :0] pwm_value_adder[2:0];
reg [2 :0] pwm_add_sub_dir;

always@(posedge sys_clk) begin
    if(reset) begin
        clk_div_counter <= 0;
    end
    else begin
        clk_div_counter <= clk_div_counter == PWM_CLK_DIV - 1 ? 0 : clk_div_counter + 1;
    end
end

always@(posedge sys_clk) begin
    if(reset) begin
        pwm_value_counter <= 0;
    end
    else if(clk_div_counter == PWM_CLK_DIV - 1) begin
        pwm_value_counter <= pwm_value_counter == PWM_DIV - 1 ? 0 : pwm_value_counter + 1;
    end
end

genvar i;
generate 
    for(i = 0; i < 3; i = i + 1) begin:three_phase_gen
        always@(posedge sys_clk) begin
            if(reset) begin
                pwm_value_adder[i] <= i * PWM_DIV / 3;
                pwm_add_sub_dir[i] <= 0;
            end
            else if((pwm_value_counter == PWM_DIV - 1) && (clk_div_counter == PWM_CLK_DIV - 1)) begin
                pwm_add_sub_dir[i] <= pwm_value_adder[i] == PWM_DIV - 2 ? 1 : pwm_value_adder[i] == 0 ? 0 : pwm_add_sub_dir[i];
                pwm_value_adder[i] <= pwm_add_sub_dir[i] ? pwm_value_adder[i] - 1 : pwm_value_adder[i] + 1;
            end
        end
    end
endgenerate


always@(posedge sys_clk) begin
    if(reset) begin
        led_red     <= 1'b0; //default red led
        led_green   <= 1'b1;
        led_blue    <= 1'b1;        
    end
    else if(~fifo_empty) begin
        led_red     <= pwm_value_counter >= pwm_value_adder[0] ? 1'b0 : 1'b1; 
        led_green   <= pwm_value_counter >= pwm_value_adder[1] ? 1'b0 : 1'b1;
        led_blue    <= pwm_value_counter >= pwm_value_adder[2] ? 1'b0 : 1'b1;
    end
    else if(usb_rstn) begin
        led_red     <= 1'b1; 
        led_green   <= 1'b1;
        led_blue    <= 1'b0; //usb connected blue led      
    end
    else begin
        led_red     <= 1'b0; //default red led
        led_green   <= 1'b1;
        led_blue    <= 1'b1;        
    end
end
    
endmodule
